package Algorithms;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import Stack.Stack;
import Stack.StackIterator;
import Templates.Operator;
import Templates.State;

/**
 * 
 * @author OC
 *
 * @param <Template>
 */
public class IterativeDeepingSearch<Template> {
	
	private boolean switcher ;
	private Stack<State<Template>> btStack ;
	private ArrayList<Operator<Template>> operators;
	private State<Template> initialState;
	private ArrayList<State<Template>> finalStates;
	private int operatorNumber =0;
	private PrintWriter logWriter = null;
	private File log;
	private int depth=0;
	
	/**
	 * 
	 * @param iState starea initiala
	 * @param fStates starile finale
	 * @param opr operatorul
	 * @param log fisierul in care se scrie succesiunea de noduri(stari) si operatori
	 */
	public IterativeDeepingSearch(State<Template> iState,ArrayList<State<Template>> fStates,ArrayList<Operator<Template>> opr,String log)
	{
		setSwitcher(false);
		btStack=new Stack<State<Template>>(100);
		initialState=iState;
		finalStates=fStates;
		if(opr != null){
			operators=opr;
			operatorNumber=operators.size()-1;
		}
		try {
			logWriter = new PrintWriter(new FileWriter(log));
		} catch (IOException e) {
			e.printStackTrace();
		}
		this.log=new File(log);
	}
	
	/**
	 * 
	 * @param thisState starea curenta
	 * @return returneaza true sau false
	 */
	private boolean found(State<Template> thisState)
	{
		for(StackIterator<State<Template>> it=new StackIterator<State<Template>>(btStack); it.hasNext();)
		{
			if(thisState.equals(it.next())) return true;
		}
		return false;
	}
	
	/**
	 * 
	 * @param thisState starea curenta
	 * @return returneaza true sau false
	 */
	private boolean solutionFound(State<Template> thisState)
	{
		for(int i=0; i<finalStates.size();i++)
			if (thisState.equals(finalStates.get(i)))
		return true;
		
		return false;
	}
	
	/**
	 * 
	 * @param operator operatorul folosit in cautare
	 * @return returneaza un intreg,ce reprezinta indexul operatorului
	 */
	private int getIndex(Operator<Template> operator)
	{
		for(int i=0;i<operators.size();i++)
		{

			if (operators.get(i).getOperatorNumber()==operator.getOperatorNumber()) return i;
		}
		return 0;

	}
	/**
	 * 
	 * @param thisState starea curenta
	 * @return returneaza true nodul este extins si false in caz contrar
	 */
	private boolean expand(State<Template> thisState)
	{
		int counter=0;
		if ( isSwitcher()){
			counter=getIndex(thisState.getRedoOperator())+1; setSwitcher(false); }
		if (getIndex(thisState.getCurrentOperator())==operatorNumber) { counter=0; }
		for(int i=counter;i<=operatorNumber;i++)
		{
			if (operators.get(i).available(thisState.getCurrentState())) 
				{
					State<Template> auxiliary=new State<Template>(operators.get(i).execute(thisState.getCurrentState()), operators.get(i));
					if (!found(auxiliary)) { 
						btStack.push(auxiliary); 
						return true; 
						}
				}
		}
		return false;
	}
	/**
	 * 
	 * @param thisState starea curenta
	 * @param level nivelul la care ne aflam
	 * @return returneaza true in cazul in care a gasit solutia si false in cazul in care nu s-a gasit
	 */
	private boolean solve(State<Template> thisState, int level)
	{
		for(depth=0;depth<level;depth++){
			if( !isSwitcher()) 
				setSwitcher(false);
			if( thisState!=null){
				if(expand(thisState))
					{
					level++;
					  if ( solutionFound( btStack.peek() ))
						  return true;
					}
					else 
					{ 
						Operator<Template> auxiliaryOperator=btStack.peek().getCurrentOperator();				
						if (btStack.size()>1)
							btStack.pop();
						State<Template> auxiliaryState=btStack.pop();
						auxiliaryState.setRedoOperator(auxiliaryOperator);
						btStack.push(auxiliaryState);
						setSwitcher(true);
				
					}		
				solve(btStack.peek(),level);
			}
		}
		return false;
	}
/**
 * 
 * @param thisState starea curenta
 */
	private void print(State<Template> thisState)
	{
		logWriter.append("\n~~~~~~~~~~~~~~~~~~~~~~~~~~~\n");
		logWriter.append(thisState.getCurrentOperator().toString());
		logWriter.append('\n');
		logWriter.append(thisState.getCurrentState().toString());
		logWriter.append('\n');


	}
	
	private void closeLog()
	{
		System.out.println("Solution found using Indepth Search... Stacktrace can be found in: " + log.getAbsolutePath());
		logWriter.close();
	}
	
	public void executeAlgorithm()
	{

		btStack.push(initialState);
		solve(initialState,0);
		for(StackIterator<State<Template>> it=new StackIterator<State<Template>>(btStack); it.hasNext();)
		{
			print(it.next());
		}
		closeLog();
	
	}
/**
 * 
 * @param switcher
 * @return returneaza valoarea lui switcher
 */
	private boolean setSwitcher(boolean switcher) {
		this.switcher = switcher;
		return switcher;
	}

	public boolean isSwitcher() {
		return switcher;
	}


}



